package cn.demoncat.util.web.config;

import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.lang.constant.CharsetConstant;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.entity.thread.LoopThread;
import cn.demoncat.util.web.convert.*;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.AbstractJsonHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * WebMvc配置(spring-mvc)
 *
 * @author 延晓磊
 *
 * @since 2017年7月21日
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer, InitializingBean, DisposableBean {


	/**
	 * 初始化事件
	 *
	 * 1、初始化Bean（属性设置）后执行，先于 @PostConstruct
	 */
	@Override
	public void afterPropertiesSet() {

	}

	/**
	 * 销毁事件
	 *
	 * 1、关闭Spring容器时调用，先于 @PreDestroy
	 */
	@Override
	public void destroy() {
		// 停止所有循环线程
		LoopThread.stopAll();
	}

	/**
	 * 消息转换器（message-converters）
	 *
	 * 1、建议由前端content-type或后端produce来指定MediaType，以便路由到特定的消息处理器
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月21日
	 */
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		// 整合转换器
		getConverters(converters);
	}
 
	/**
	 * 参数类型转换器(conversionService)
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月21日
	 */
	@Override
	public void addFormatters(FormatterRegistry registry) {
		ConvertUtil.addFormatters(registry);
	}
	
	/**
	 * CORS 跨域设置
	 * 
	 * 注：如果请求中附带了其它headers，浏览器将发起OPTIONS预请求。建议通过Nginx直接处理OPTIONS请求，以便只有正常请求进入应用服务器。
	 * 注：因为后台已进行CORS配置，所以Nginx中不能对反向代理的请求(没有直接return)进行处理，否则会叠加响应头信息而出错（如 *,*）
	 * 
	 * @param registry
	 *
	 * @author 延晓磊
	 *
	 * @since 2018年7月3日
	 *
	 */
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		// PATH > 来源 > 请求方法 > 请求头 > 凭证(cookie)
		registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*").allowCredentials(true);
	}

	/**
	 * 获取消息转换器
	 * 
	 * @param converters	当前消息转换器
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年12月26日
	 */
	public static List<HttpMessageConverter<?>> getConverters(List<HttpMessageConverter<?>> converters){
		// 当前转换器
		if (converters == null) {
			converters = new ArrayList<>();
		}else {
			// 去除默认的String和JSON消息转换器：参考 WebMvcConfigurationSupport.addDefaultHttpMessageConverters
			HttpMessageConverter<?> converter;
			Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
			while (iterator.hasNext()) {
				converter = iterator.next();
				// String转换器，Jackson转换器，Gson/Jsonb转换器
				if (converter instanceof StringHttpMessageConverter 
						|| converter instanceof MappingJackson2HttpMessageConverter 
						|| converter instanceof AbstractJsonHttpMessageConverter) {
					iterator.remove();
				}
			}
		}
		// 临时List
		List<HttpMessageConverter<?>> tmp = new ArrayList<>(converters);
		// 清理
		converters.clear();
		// 添加String消息解析：text/plain , */*(String类型返回值)
		converters.add(createStrConvert());
		// 添加JSON消息解析：application/json (Object类型返回值)
		converters.add(createFastJsonConvert());
		// 添加原转换器：将String、JSON转换器排在前面
		converters.addAll(tmp);
		return converters;
	}
	
	/**
	 * 功能描述：创建字符串消息转换器
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月21日
	 */
	private static StringHttpMessageConverter createStrConvert() {
		// 支持的响应类型：text/plain , */*
		return new StringHttpMessageConverter(CharsetConstant.UTF8_CHARSET);
	}
	
	/**
	 * 功能描述：创建FastJSON消息转换器
	 * 
	 * 注： FastJSON序列化字符串时会进行转义，所以不能解析String类型返回值
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月21日
	 */
	private static FastJsonHttpMessageConverter createFastJsonConvert() {
		// 创建JSON消息转换器，默认charset=utf-8,mediaType=*/*
		FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
		// 支持的响应类型：application/json , application/vnd.spring-boot.actuator.v2+json
		List<MediaType> medias = new ArrayList<>();
		medias.add(MediaType.APPLICATION_JSON);
		medias.add(new MediaType("application", "json", CharsetConstant.UTF8_CHARSET));
		medias.add(new MediaType("application", "*+json"));
		medias.add(new MediaType("application", "*+json",CharsetConstant.UTF8_CHARSET));
		fastConverter.setSupportedMediaTypes(medias);
		// 创建FastJSON配置(解析时格式化数据)
		FastJsonConfig fastJsonConfig = new FastJsonConfig();
		// 序列化配置
		fastJsonConfig.setSerializerFeatures(JsonUtil.FEATURES_SHORT);
		fastJsonConfig.setDateFormat(DateConstant.DATE_TIME_PATTERN);
		// 添加配置到消息转换器
		fastConverter.setFastJsonConfig(fastJsonConfig);
		return fastConverter;
	}
	

}
